/** Object
**
** Base class of objects passed to RenderOneFrame
**
** Note: If your game objects have these fields, use them instead.
** Or, put a member variable in here which points to one of your objects.
**
** @version: $Id: Object.cpp 64 2010-11-25 20:46:39Z NeurobaticsTim@gmail.com $
*/
#include <assert.h>

#include "Object.h"
#include "Renderer.h"


/*****************************************************\
* Object constructor                                  *
\*****************************************************/
Object::Object(const D3DXVECTOR3& cvPosition, const float cfScale)
  : m_pMesh(NULL)
  , m_vPosition(cvPosition)
  , m_fScale(cfScale)
{
	m_vMaterials.clear();
	UpdateWorldTransform();
}


/*****************************************************\
* Destructor                                          *
\*****************************************************/
Object::~Object()
{
	/* Deallocate all the things we created */
	for each (MESHMATERIAL* pM in m_vMaterials)
	{
		assert(NULL != pM);

		/* We allocated the materials copies, so we free them */
		if (NULL != pM->m_pMaterial)
		{
			delete pM->m_pMaterial;
			pM->m_pMaterial = NULL;
		}

		/* pTexture is freed by whomever allocated it.
		** We only used a pointer here.
		*/
		delete pM;
		pM = NULL;
	}
	m_vMaterials.clear();
}


/*****************************************************\
* Update each frame                                   *
\*****************************************************/
bool Object::Update(const double cdDeltaTime)
{
	return true;
}


/*****************************************************\
* Add Material: put [material,texture] in list        *
\*****************************************************/
void Object::AddMaterial(D3DMATERIAL9* pMaterial, IDirect3DTexture9* pTexture)
{
	/* Create new meshmaterial table entry */
	MESHMATERIAL* pMeshMaterial = new MESHMATERIAL;
	assert(NULL != pMeshMaterial);
	ZeroMemory(pMeshMaterial, sizeof(MESHMATERIAL));

	/* Create new material as a copy */
	pMeshMaterial->m_pMaterial = new D3DMATERIAL9;
	assert(NULL != pMeshMaterial->m_pMaterial);
	ZeroMemory(pMeshMaterial->m_pMaterial, sizeof(D3DMATERIAL9));
	*(pMeshMaterial->m_pMaterial) = *pMaterial;

	/* Texture is a pointer which isn't changed outside */
	pMeshMaterial->m_pTexture = pTexture;

	/* Save new {material, texture} in list */
	m_vMaterials.push_back(pMeshMaterial);
}


/*****************************************************\
* Get Material: get [material,texture] for mesh       *
\*****************************************************/
void Object::GetMaterial(unsigned n, D3DMATERIAL9** ppMaterial, IDirect3DTexture9** ppTexture)
{
	/* Get material information at index n for subset n */
	MESHMATERIAL* pM = m_vMaterials[n];

	/* Does the caller want the material information? */
	if (NULL != ppMaterial)
	{
		*ppMaterial = pM->m_pMaterial;
	}
	/* Do they want the texture information? */
	if (NULL != ppTexture)
	{
		*ppTexture = pM->m_pTexture;
	}
}


/*****************************************************\
* Set position and update WORLD transform             *
\*****************************************************/
void Object::SetPosition(const D3DXVECTOR3& cvPosition)
{
	m_vPosition = cvPosition;
	UpdateWorldTransform();
}


/*****************************************************\
* Set display scale and update WORLD transform        *
\*****************************************************/
void Object::SetScale(const float cfScale)
{
	m_fScale = cfScale;
	UpdateWorldTransform();
}


/*****************************************************\
* Get access to object's mesh                         *
\*****************************************************/
ID3DXMesh* Object::GetMesh()
{
	return m_pMesh;
}


/*****************************************************\
* Get radius of computed bounding sphere              *
\*****************************************************/
float Object::GetBoundingRadius()
{
	return m_fBoundingRadius;
}


/*****************************************************\
* Draw this object                                    *
\*****************************************************/
void Object::Render()
{
	/* Actually, ask the renderer to draw it
	** This trick is so that objects always know how to render themselves
	*/
	Renderer::Instance()->RenderObject(this);
}


/*****************************************************\
* Update WORLD transform so drawn in correct place    *
\*****************************************************/
void Object::UpdateWorldTransform()
{
	/* Scale in all three dimensions */
	D3DXMATRIX mScale;
	D3DXMatrixScaling(&mScale, m_fScale, m_fScale, m_fScale);

	/* Translate to correct position in 3-space */
	D3DXMATRIX mTranslate;
	D3DXMatrixTranslation(&mTranslate, m_vPosition.x, m_vPosition.y, m_vPosition.z);

	/* Combine: scale then translate */
	D3DXMatrixMultiply(&m_mWorld, &mScale, &mTranslate);
}


/*****************************************************\
* Set WORLD transform                                 *
\*****************************************************/
void Object::SetWorldTransform(const D3DXMATRIX mWorld)
{
	m_mWorld = mWorld;
}
